/* *
 * --ライセンスについて--
 *
 * 「本ファイルの内容は Mozilla Public License Version 1.1 (「本ライセンス」)
 * の適用を受けます。
 * 本ライセンスに従わない限り本ファイルを使用することはできません。
 * 本ライセンスのコピーは http://www.mozilla.org/MPL/ から入手できます。
 *
 * 本ライセンスに基づき配布されるソフトウェアは、「現状のまま」で配布されるものであり、
 * 明示的か黙示的かを問わず、いかなる種類の保証も行われません。
 * 本ライセンス上の権利および制限を定める具体的な文言は、本ライセンスを参照してください。
 *
 * オリジナルコードおよび初期開発者は、N_H (h.10x64@gmail.com) です。
 *
 * N_H によって作成された部分の著作権表示は次のとおりです。
 *
 * Copyright (C)N_H 2012
 *
 * このファイルの内容は、上記に代えて、
 * GNU General License version2 以降 (以下 GPL とする)、
 * GNU Lesser General Public License Version 2.1 以降 (以下 LGPL とする)、
 * の条件に従って使用することも可能です。
 * この場合、このファイルの使用には上記の条項ではなく GPL または LGPL の条項が適用されます。
 * このファイルの他者による使用を GPL または LGPL の条件によってのみ許可し、
 * MPL による使用を許可したくない対象者は、上記の条項を削除することでその意思を示し、
 * 上記条項を GPL または LGPL で義務付けられている告知およびその他の条項に置き換えてください。
 * 対象者が上記の条項を削除しない場合、
 * 受領者は MPL または GPL または LGPL ライセンスのいずれによってもこのファイルを
 * 使用することができます。」
 *
 * -- License --
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License。You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND、either express or implied。See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Initial Developer of the Original Code is
 *   N_H (h.10x64@gmail.com).
 *
 * Portions created by the Initial Developer are Copyright (C)N_H 2012
 * the Initial Developer。All Rights Reserved.
 *
 * Alternatively、the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL")、or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above。If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL、and not to allow others to
 * use your version of this file under the terms of the MPL、indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL。If you do not delete
 * the provisions above、a recipient may use your version of this file under
 * the terms of any one of the MPL、the GPL or the LGPL.
 *
 * */
package com.magiciansforest.audio.soundrenderer.logic;

import com.magiciansforest.audio.soundrenderer.event.*;
import com.magiciansforest.audio.soundrenderer.logic.sound.AudioRenderThread;
import com.magiciansforest.audio.soundrenderer.logic.sound.SoundSource;
import java.io.File;
import java.io.IOException;

/**
 *
 * @author N_H
 */
public class MMDSoundSource implements CharacterChangeEventListener, MovieStatusChangeEventListener, FramePositionChangeEventListener {

    private long parentID;
    private Movie mov;
    private String boneName;
    private long startFrame = 0, endFrame = 0;
    private long framePosition;
    protected SoundSource src;

    public MMDSoundSource(long id, Movie mov, AudioRenderThread thread, long parentID, String boneName) {
        src = new SoundSource(id, thread);
        this.parentID = parentID;
        this.mov = mov;
        this.boneName = boneName;
        mov.addCharacterChangeEventListener(this);
        mov.addFramePositionChangeEvnetListener(this);
        mov.addMovieStatusChangeEventListener(this);
    }

    @Override
    public void characterChange(CharacterChangeEvent e) {
        if (e.getStatus() == MovieChangeEvent.REMOVED && e.getID() == parentID) {
            mov.bind(src.getID(), -1, null);
        }
    }

    @Override
    public void changeFramePosition(FramePositionChangeEvent e) {
        setStatus(e.getReason() != FramePositionChangeEvent.UPDATE);
    }

    @Override
    public void changeMovieStatus(MovieStatusChangeEvent e) {
        setStatus(false);
    }

    private void setStatus(boolean setFrame) {
        long pos = movieFrameToSoundFrame(src.getSampleRate(), mov.getMovieFramePosition());
        if (setFrame) {
            setPosition(pos);
        }
        framePosition = pos;

        if (src.getStatus() != SoundSource.STOPPED) {
            if (mov.getMovieStatus() == Movie.STARTED || mov.getMovieStatus() == Movie.RENDERING) {
                if (framePosition < startFrame) {
                    src.pause();
                } else if (framePosition >= startFrame && framePosition < endFrame) {
                    src.play();
                } else {
                    src.pause();
                }
            } else if (mov.getMovieStatus() == Movie.PAUSED && src.getStatus() != SoundSource.PAUSED) {
                src.pause();
            } else if (mov.getMovieStatus() == Movie.STOPPED) {
                src.stop();
            }
        }
    }

    private void setPosition(long frameNo) {
        try {
            if (frameNo < startFrame) {
                src.setFrame(0);
            } else if (frameNo >= startFrame && frameNo < endFrame) {
                if (frameNo - startFrame < src.getLastFrameNo()) {
                    src.setFrame(frameNo - startFrame);
                }
            } else if (frameNo > endFrame) {
                src.setFrame(src.getLastFrameNo());
            }
        } catch (IOException ioe) {
            System.err.println(ioe.getMessage());
        }
    }

    public long getParentID() {
        return parentID;
    }

    void setParentID(long modelID) {
        this.parentID = modelID;
    }

    void setWavFile(String filePath) throws IOException {
        src.setWavFile(new File(filePath));
        setStartFrame(0);
        setEndFrame((int) Math.round(TimeKeeper.MMD_FPS * src.getLastFrameNo() / src.getSampleRate()));
    }

    public String getWavFilePath() {
        return src.getWavFilePath();
    }

    public String getBoneName() {
        return boneName;
    }

    void setBoneName(String boneName) {
        this.boneName = boneName;
    }

    public String getName() {
        return src.getName();
    }

    void setName(String name) {
        src.setName(name);
    }

    public long getID() {
        return src.getID();
    }

    public SoundSource getSource() {
        return this.src;
    }

    boolean setStartFrame(int movieFrame) {
        long next = movieFrameToSoundFrame(src.getSampleRate(), movieFrame);
        if (next != startFrame && next >= 0) {
            startFrame = movieFrameToSoundFrame(src.getSampleRate(), movieFrame);
            if (startFrame > endFrame) {
                endFrame = startFrame;
            }
            return true;
        } else {
            return false;
        }
    }

    boolean setEndFrame(int movieFrame) {
        long next = movieFrameToSoundFrame(src.getSampleRate(), movieFrame);
        if (next != endFrame && next >= 0) {
            endFrame = next;
            if (endFrame < startFrame) {
                startFrame = endFrame;
            }
            return true;
        } else {
            return false;
        }
    }

    public int getStartFrame() {
        return soundFrameToMovieFrame(src.getSampleRate(), startFrame);
    }

    void setSourceStartFrame(int movieFrame) {
        src.setStartFrame(movieFrameToSoundFrame(src.getSampleRate(), movieFrame));
    }

    public int getSourceStartFrame() {
        return soundFrameToMovieFrame(src.getSampleRate(), src.getStartFrame());
    }

    public int getEndFrame() {
        return soundFrameToMovieFrame(src.getSampleRate(), endFrame);
    }

    void setSourceEndTime(int movieFrame) {
        src.setEndFrame(movieFrameToSoundFrame(src.getSampleRate(), movieFrame));
    }

    public int getSourceEndFrame() {
        return soundFrameToMovieFrame(src.getSampleRate(), src.getEndFrame());
    }

    public void setVolume(double volume) {
        this.src.setVolume(volume);
    }

    public double getVolume() {
        return src.getVolume();
    }

    private long movieFrameToSoundFrame(float sampleRate, int movieFrame) {
        double time = (double) movieFrame / TimeKeeper.MMD_FPS;
        return Math.round(sampleRate * time);
    }

    private int soundFrameToMovieFrame(float sampleRate, long soundFrame) {
        double time = (double) soundFrame / sampleRate;
        return (int) Math.round(TimeKeeper.MMD_FPS * time);
    }
}
